package com.executor.gateway.core;

import com.executor.gateway.core.exception.CircuitBreakerOpenException;
import com.executor.gateway.service.MetricsService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.ConnectException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;

/**
 * 全局异常处理器
 * 支持传统模式和增强模式两种处理方式
 * 兼容WebFlux响应式编程模型
 *
 * @author executor
 */
@Component
@Slf4j
@Order(-1)
public class GlobalExceptionHandler implements ErrorWebExceptionHandler {

    @Autowired(required = false)
    private MetricsService metricsService;

    private final ObjectMapper objectMapper = new ObjectMapper();

    @Getter
    @Setter
    private boolean enhancedMode = true;

    @Getter
    @Setter
    private boolean recordMetrics = true;

    @Getter
    @Setter
    private boolean includeTraceId = true;

    @Getter
    @Setter
    private boolean includeDetailedError = true;

    @Getter
    @Setter
    private boolean hideSensitiveInfoInProduction = true;

    @Override
    @NonNull
    public Mono<Void> handle(@NonNull ServerWebExchange exchange, @NonNull Throwable ex) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        if (response.isCommitted()) {
            return Mono.error(ex);
        }

        // 根据是否启用增强模式选择处理方式
        Mono<Void> handleResult;
        if (enhancedMode) {
            handleResult = handleEnhanced(exchange, ex);
        } else {
            handleResult = handleLegacy(exchange, ex);
        }

        // 记录异常指标（如果启用增强模式且指标服务可用）
        if (enhancedMode && recordMetrics && metricsService != null) {
            return recordExceptionMetrics(request, ex)
                    .then(handleResult)
                    .onErrorResume(metricsError -> {
                        log.warn("Failed to record exception metrics", metricsError);
                        return handleResult;
                    });
        } else {
            return handleResult;
        }
    }

    /**
     * 增强模式异常处理
     */
    private Mono<Void> handleEnhanced(ServerWebExchange exchange, Throwable ex) {
        ServerHttpResponse response = exchange.getResponse();
        ServerHttpRequest request = exchange.getRequest();

        // 确定HTTP状态码
        HttpStatus status = determineHttpStatus(ex);
        response.setStatusCode(status);

        // 设置响应头
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);

        // 构建错误响应
        Map<String, Object> errorResponse = buildEnhancedErrorResponse(request, ex, status);

        return writeJsonResponse(response, errorResponse);
    }

    /**
     * 传统模式异常处理
     */
    private Mono<Void> handleLegacy(ServerWebExchange exchange, Throwable ex) {
        ServerHttpResponse response = exchange.getResponse();

        // 设置基本错误状态
        response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);

        // 构建简单错误响应
        Map<String, Object> errorResponse = new HashMap<>();
        errorResponse.put("success", false);
        errorResponse.put("message", "Internal Server Error");
        errorResponse.put("code", HttpStatus.INTERNAL_SERVER_ERROR.value());
        errorResponse.put("timestamp", LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));

        return writeJsonResponse(response, errorResponse);
    }

    /**
     * 写入JSON响应
     */
    private Mono<Void> writeJsonResponse(ServerHttpResponse response, Map<String, Object> errorResponse) {
        try {
            String jsonResponse = objectMapper.writeValueAsString(errorResponse);
            DataBufferFactory bufferFactory = response.bufferFactory();
            DataBuffer buffer = bufferFactory.wrap(jsonResponse.getBytes(StandardCharsets.UTF_8));
            return response.writeWith(Mono.just(buffer));
        } catch (JsonProcessingException e) {
            log.error("Error serializing error response", e);
            // 降级处理：返回简单的错误响应
            String fallbackResponse = "{\"success\":false,\"message\":\"Internal Server Error\",\"code\":500}";
            DataBufferFactory bufferFactory = response.bufferFactory();
            DataBuffer buffer = bufferFactory.wrap(fallbackResponse.getBytes(StandardCharsets.UTF_8));
            return response.writeWith(Mono.just(buffer));
        }
    }

    /**
     * 确定HTTP状态码
     */
    private HttpStatus determineHttpStatus(Throwable ex) {
        if (ex instanceof ResponseStatusException) {
            return ((ResponseStatusException) ex).getStatus();
        } else if (ex instanceof CircuitBreakerOpenException) {
            return HttpStatus.SERVICE_UNAVAILABLE;
        } else if (ex instanceof TimeoutException) {
            return HttpStatus.GATEWAY_TIMEOUT;
        } else if (ex instanceof ConnectException) {
            return HttpStatus.BAD_GATEWAY;
        } else if (ex instanceof IllegalArgumentException) {
            return HttpStatus.BAD_REQUEST;
        } else if (ex instanceof SecurityException) {
            return HttpStatus.UNAUTHORIZED;
        } else {
            return HttpStatus.INTERNAL_SERVER_ERROR;
        }
    }

    /**
     * 构建增强错误响应
     */
    private Map<String, Object> buildEnhancedErrorResponse(ServerHttpRequest request, Throwable ex, HttpStatus status) {
        Map<String, Object> errorResponse = new HashMap<>();

        // 基本信息
        errorResponse.put("success", false);
        errorResponse.put("code", status.value());
        errorResponse.put("status", status.getReasonPhrase());
        errorResponse.put("timestamp", LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));

        // 请求信息
        errorResponse.put("path", request.getPath().value());
        errorResponse.put("method", request.getMethod().name());

        // 追踪ID
        if (includeTraceId) {
            String traceId = request.getHeaders().getFirst("X-Trace-ID");
            if (traceId != null) {
                errorResponse.put("traceId", traceId);
            }
        }

        // 错误消息
        String message = getErrorMessage(ex);
        errorResponse.put("message", message);

        // 详细错误信息（非生产环境或配置允许时）
        if (includeDetailedError && (!hideSensitiveInfoInProduction || !isProductionEnvironment())) {
            errorResponse.put("exception", ex.getClass().getSimpleName());
            if (ex.getCause() != null) {
                errorResponse.put("cause", ex.getCause().getClass().getSimpleName());
            }
        }

        return errorResponse;
    }

    /**
     * 获取错误消息
     */
    private String getErrorMessage(Throwable ex) {
        if (ex instanceof CircuitBreakerOpenException) {
            return "Service temporarily unavailable due to circuit breaker";
        } else if (ex instanceof TimeoutException) {
            return "Request timeout";
        } else if (ex instanceof ConnectException) {
            return "Service connection failed";
        } else if (ex instanceof ResponseStatusException) {
            return ((ResponseStatusException) ex).getReason();
        } else {
            return ex.getMessage() != null ? ex.getMessage() : "Internal Server Error";
        }
    }

    /**
     * 判断是否为生产环境
     */
    private boolean isProductionEnvironment() {
        String profile = System.getProperty("spring.profiles.active", "");
        return profile.contains("prod") || profile.contains("production");
    }

    /**
     * 记录异常指标
     */
    private Mono<Void> recordExceptionMetrics(ServerHttpRequest request, Throwable ex) {
        if (metricsService == null) {
            return Mono.empty();
        }

        try {
            String path = request.getPath().value();
            String exceptionType = ex.getClass().getSimpleName();

            // 记录异常计数
            return metricsService.incOneCount2MetricsMap(path, "exception_" + exceptionType)
                    .onErrorResume(error -> {
                        log.warn("Failed to record exception metrics for path: {}", path, error);
                        return Mono.empty();
                    });
        } catch (Exception e) {
            log.warn("Error in recordExceptionMetrics", e);
            return Mono.empty();
        }
    }
}
